package task

import (
	"context"
	"fmt"
	"pids-cloud-server/constants"
	"pids-cloud-server/entity"
	"pids-cloud-server/logging"
	"strconv"
	"strings"
	"sync"
	"time"

	"pids-cloud-server/utils"

	"github.com/google/uuid"
	"github.com/redis/go-redis/v9"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

var redisClientPool sync.Map // 缓存 Redis 客户端池

func RedisTask() {
	logging.Info("---------定时采集Redis数据任务开始---------")
	start := time.Now()
	defer func() {
		if r := recover(); r != nil {
			logging.Error("Redis任务发生严重错误: ", r)
		}
		logging.Info("定时采集Redis数据任务结束---------耗时：", time.Since(start))
	}()

	// 获取数据库连接
	db := utils.GetDB()
	if db == nil {
		logging.Error("数据库连接失败，跳过Redis采集任务")
		return
	}

	var alarmInfoList []entity.AlarmInfo
	if tx := db.Where("status = ? AND type = ?", entity.ACTIVE, entity.REDIS_ERROR).Find(&alarmInfoList); tx.Error != nil {
		logging.Error("初始化REDIS告警数据失败: ", tx.Error)
		return
	}

	// 初始化告警信息映射
	for _, alarmInfo := range alarmInfoList {
		constants.REDIS_ALARM_INFO_MAP.Store(alarmInfo.HostIP, alarmInfo)
	}

	// 获取数据库中所有 Redis 信息
	var redisInfoList []entity.RedisInfo
	if err := db.Model(&entity.RedisInfo{}).Find(&redisInfoList).Error; err != nil {
		logging.Error("获取 Redis 信息失败: ", err)
		return
	}

	// 如果没有 Redis 实例需要监控，直接返回
	if len(redisInfoList) == 0 {
		logging.Info("没有找到需要监控的Redis实例")
		return
	}

	redisChannel := make(chan entity.RedisInfo, len(redisInfoList))
	alarmChannel := make(chan entity.AlarmInfo, len(redisInfoList))
	var wg sync.WaitGroup
	//遍历redisInfoList，采集每个Redis实例的数据
	for _, redisInfo := range redisInfoList {
		wg.Add(1)
		go func(redisInfo entity.RedisInfo) {
			var alarmInfo *entity.AlarmInfo
			defer wg.Done()
			//创建redis客户端连接池
			client, err := getRedisClients(redisInfo)
			if err != nil {
				logging.Error("创建redis客户端连接池失败: ", err)
				alarmInfo = generateRedisAlarm(redisInfo)
				if alarmInfo != nil {
					alarmChannel <- *alarmInfo
				}
				return
			}
			ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
			defer cancel()
			//测试redis连接
			_, err = client.Ping(ctx).Result()
			if err != nil {
				logging.Errorf("redis连接失败: %s,错误:%v", redisInfo.IP, err)
				alarmInfo = generateRedisAlarm(redisInfo)
				if alarmInfo != nil {
					alarmChannel <- *alarmInfo
				}
				return
			}
			// 获取 Redis 信息
			info, err := client.Info(ctx).Result()
			if err != nil {
				logging.Error("获取 Redis 信息失败: ", err)
				return
			}
			//解析redis信息
			parseRedisInfo(&redisInfo, info)
			// 在 Redis 信息采集逻辑中获取 maxClients
			maxClients, err := getRedisMaxClients(client)
			if err != nil {
				logging.Error("获取 Redis maxClients 失败: ", err)
			} else {
				redisInfo.MaxClients = maxClients
				logging.Infof("Redis %s 最大连接数: %d", redisInfo.Name, maxClients)
			}
			alarmInfo = recoverRedisAlarm(redisInfo)
			if alarmInfo != nil {
				alarmChannel <- *alarmInfo
			}
			redisChannel <- redisInfo // 将redis信息发送到 channel
		}(redisInfo)
	}
	// 优化 channel 关闭逻辑
	go func() {
		wg.Wait()
		// 安全关闭 channel
		defer func() {
			recover() // 防止 channel 已关闭时的 panic
		}()
		close(redisChannel)
		close(alarmChannel)
	}()

	// 收集数据的优化
	var allRedisInfos []entity.RedisInfo
	var allAlarms []entity.AlarmInfo
	for redisInfo := range redisChannel {
		allRedisInfos = append(allRedisInfos, redisInfo)
	}
	// 调用 UpsertRedisInfo
	if err := UpsertRedisInfo(db, allRedisInfos); err != nil {
		logging.Errorf("Failed to upsert redis info:%v", err)
	} else {
		logging.Infof("Upsert redis信息success!")
	}
	for alarmInfo := range alarmChannel {
		allAlarms = append(allAlarms, alarmInfo)
	}
	//保存、更新告警数据
	if len(allAlarms) > 0 {
		tx := db.Clauses(clause.OnConflict{
			Columns:   []clause.Column{{Name: "id"}},
			DoUpdates: clause.AssignmentColumns([]string{"status", "modify_time", "level_id", "detail"}),
		}).CreateInBatches(&allAlarms, 100)
		if tx.Error != nil {
			logging.Error("保存REDIS告警数据失败：", tx.Error)
		} else {
			logging.Info("成功更新告警信息，更新数量: ", len(allAlarms))
		}
	}
}

// 恢复告警记录
func recoverRedisAlarm(redisInfo entity.RedisInfo) (alarmInfo *entity.AlarmInfo) {
	value, exists := constants.REDIS_ALARM_INFO_MAP.Load(redisInfo.IP)
	//存在旧告警，且没有产生新告警时
	if exists {
		// Redis 变正常，删除告警，并生成恢复告警
		logging.Infof("Redis %s:%s 恢复正常, 清除告警", redisInfo.IP, redisInfo.Name)
		if alarmValue, ok := value.(entity.AlarmInfo); ok {
			alarmInfo = &alarmValue // 取地址
		} else {
			return nil // 避免 panic
		}
		alarmInfo.Status = entity.INACTIVE
		alarmInfo.ModifyTime = time.Now()
		constants.REDIS_ALARM_INFO_MAP.Delete(redisInfo.IP)
	}
	return alarmInfo
}

// 产生redis告警记录
func generateRedisAlarm(redisInfo entity.RedisInfo) (alarmInfo *entity.AlarmInfo) {
	value, exists := constants.REDIS_ALARM_INFO_MAP.Load(redisInfo.IP)
	if exists {
		//当前redis实例存在告警记录，则更新告警记录
		if alarmValue, ok := value.(entity.AlarmInfo); ok {
			alarmInfo = &alarmValue // 取地址
		} else {
			return nil // 避免 panic
		}
		alarmInfo.Detail = redisInfo.Name + ":" + redisInfo.IP + "Redis服务异常"
		alarmInfo.ModifyTime = time.Now()
		constants.REDIS_ALARM_INFO_MAP.Store(redisInfo.IP, alarmInfo)
	} else {
		//当前redis实例不存在告警记录，则新增告警记录
		alarmInfo = &entity.AlarmInfo{
			ID:         uuid.NewString(),
			Type:       entity.REDIS_ERROR,
			Detail:     redisInfo.Name + ":" + redisInfo.IP + "Redis服务异常",
			HostIP:     redisInfo.IP,
			Flag:       entity.Server,
			Status:     entity.ACTIVE,
			LevelID:    1,
			CreateTime: time.Now(),
			ModifyTime: time.Now(),
		}
		constants.REDIS_ALARM_INFO_MAP.Store(redisInfo.IP, alarmInfo)
	}
	return alarmInfo
}

func getRedisMaxClients(client *redis.Client) (int64, error) {
	result, err := client.ConfigGet(context.Background(), "maxclients").Result()
	if err != nil {
		return 0, err
	}
	if len(result) >= 1 {
		if maxClients, ok := result["maxclients"]; ok {
			return strconv.ParseInt(maxClients, 10, 64)
		}
	}
	return 0, fmt.Errorf("无法获取 maxclients 配置")
}

// UpsertRedisInfo 使用 OnConflict 实现插入或更新
func UpsertRedisInfo(db *gorm.DB, redisInfoList []entity.RedisInfo) error {
	// 插入或更新操作
	err := db.Clauses(clause.OnConflict{
		Columns: []clause.Column{{Name: "ip"}}, // 唯一约束字段
		DoUpdates: clause.AssignmentColumns([]string{"version", "redis_mode", "up_in_seconds", "up_in_days", "connected_clients", "max_clients",
			"used_memory", "max_memory", "aof_enabled", "role", "cluster_enabled", "used_cpu_sys", "used_cpu_user", "update_time"}), // 要更新的字段
	}).CreateInBatches(&redisInfoList, 100).Error
	return err
}
func parseRedisInfo(redisInfo *entity.RedisInfo, redisInfoStr string) {
	// 保持解析逻辑不变
	lines := strings.Split(redisInfoStr, "\n")
	for _, line := range lines {
		if strings.HasPrefix(line, "#") || !strings.Contains(line, ":") {
			continue
		}
		parts := strings.SplitN(line, ":", 2)
		key := parts[0]
		value := strings.TrimSpace(parts[1])
		switch key {
		case "redis_version":
			redisInfo.Version = value
		case "redis_mode":
			redisInfo.RedisMode = value
		case "uptime_in_seconds":
			redisInfo.UpInSeconds, _ = strconv.ParseInt(value, 10, 64)
		case "uptime_in_days":
			redisInfo.UpInDays, _ = strconv.ParseInt(value, 10, 64)
		case "connected_clients":
			redisInfo.ConnectedClients, _ = strconv.ParseInt(value, 10, 64)
		case "maxclients":
			redisInfo.MaxClients, _ = strconv.ParseInt(value, 10, 64)
		case "used_memory":
			redisInfo.UsedMemory, _ = strconv.ParseInt(value, 10, 64)
		case "maxmemory":
			redisInfo.MaxMemory, _ = strconv.ParseInt(value, 10, 64)
		case "aof_enabled":
			if value == "1" {
				redisInfo.AofEnabled = 1
			} else {
				redisInfo.AofEnabled = 0
			}
		case "role":
			redisInfo.Role = value
		case "cluster_enabled":
			if value == "1" {
				redisInfo.ClusterEnabled = 1
			} else {
				redisInfo.ClusterEnabled = 0
			}
		case "used_cpu_sys":
			redisInfo.UsedCpuSys, _ = strconv.ParseInt(strings.Split(value, ".")[0], 10, 64)
		case "used_cpu_user":
			redisInfo.UsedCpuUser, _ = strconv.ParseInt(strings.Split(value, ".")[0], 10, 64)
		}
	}
}

func getRedisClients(redisInfo entity.RedisInfo) (*redis.Client, error) {
	key := fmt.Sprintf("%s:%s", redisInfo.IP, redisInfo.Port)
	client, ok := redisClientPool.Load(key)
	if !ok {
		options := &redis.Options{
			Addr:         key,
			Password:     redisInfo.Password, // 密码
			DB:           0,                  // 默认数据库
			PoolSize:     10,                 // 连接池大小
			MinIdleConns: 2,                  // 最小空闲连接数
		}
		newClient := redis.NewClient(options)
		redisClientPool.Store(key, newClient)
		return newClient, nil
	}
	return client.(*redis.Client), nil
}
